using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Reflection;

namespace Toenda.Foundation.PluginBase {
	/// <summary>
	/// Provides plugin types.
	/// </summary>
	public enum PluginType {
		/// <summary>
		/// The default type.
		/// </summary>
		Default = 0,
		/// <summary>
		/// A print plugin.
		/// </summary>
		Print = 1,
		/// <summary>
		/// A window form plugin.
		/// </summary>
		WindowForm = 2
	}

	/// <summary>
	/// Class PluginProvider
	/// </summary>
	public class PluginProvider {
		private string _fileName = "";

		/// <summary>
		/// Default constructor
		/// </summary>
		public PluginProvider(string fileName) {
			if(fileName == null) {
				throw new ArgumentNullException("fileName", "The parameter fileName cannot be null.");
			}
			this._fileName = fileName;

			if(!File.Exists(this._fileName)) {
				throw new ComponentNotFoundException(fileName);
			}
		}

		/// <summary>
		/// Loads a component from type T with type [typeName] from file [fileName].
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="fileName"></param>
		/// <param name="typeName"></param>
		/// <returns></returns>
		public static T LoadAssembly<T>(string fileName, string typeName) {
			PluginProvider loader = new PluginProvider(fileName);
			return loader.Load<T>(typeName);
		}

		/// <summary>
		/// Load a plugin
		/// </summary>
		/// <param name="fileName"></param>
		/// <param name="typeName">The typename of the class to load</param>
		/// <exception cref="ArgumentNullException">If the parameter filename is null.</exception>
		public static IPlugin LoadPlugin(string fileName, string typeName) {
			PluginProvider loader = new PluginProvider(fileName);
			return loader.Load<IPlugin>(typeName);
		}

		/// <summary>
		/// Load a print plugin
		/// </summary>
		/// <param name="fileName"></param>
		/// <param name="typeName">The typename of the class to load</param>
		/// <exception cref="ArgumentNullException">If the parameter filename is null.</exception>
		public static IPluginPrint LoadPrintPlugin(string fileName, string typeName) {
			PluginProvider loader = new PluginProvider(fileName);
			return loader.Load<IPluginPrint>(typeName);
		}

		/// <summary>
		/// Load a window form plugin
		/// </summary>
		/// <param name="fileName"></param>
		/// <param name="typeName">The typename of the class to load</param>
		/// <exception cref="ArgumentNullException">If the parameter filename is null.</exception>
		public static IPluginWindowForm LoadWindowFormPlugin(string fileName, string typeName) {
			PluginProvider loader = new PluginProvider(fileName);
			return loader.Load<IPluginWindowForm>(typeName);
		}

		/// <summary>
		/// Load a plugin
		/// </summary>
		/// <param name="typeName">The typename of the class to load</param>
		/// <exception cref="ArgumentNullException">If the parameter filename is null.</exception>
		public IPlugin Load(string typeName) {
			return this.Load<IPlugin>(typeName);
		}

		/// <summary>
		/// Load a print plugin
		/// </summary>
		/// <param name="typeName">The typename of the class to load</param>
		/// <exception cref="ArgumentNullException">If the parameter filename is null.</exception>
		public IPluginPrint LoadPrint(string typeName) {
			return this.Load<IPluginPrint>(typeName);
		}

		/// <summary>
		/// Load a window form plugin
		/// </summary>
		/// <param name="typeName">The typename of the class to load</param>
		/// <exception cref="ArgumentNullException">If the parameter filename is null.</exception>
		public IPluginWindowForm LoadWindowForm(string typeName) {
			return this.Load<IPluginWindowForm>(typeName);
		}

		/// <summary>
		/// Loads the assembly.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="typeName">The typename.</param>
		/// <returns></returns>
		public T Load<T>(string typeName) {
			if(this._fileName == null) {
				throw new ArgumentNullException("fileName", "The parameter fileName cannot be null (constructor).");
			}

			Assembly plugin = Assembly.LoadFrom(this._fileName);

			string name = plugin.GetName().ToString();
			string[] namelist = name.Split(',');
			name = namelist[0].Trim();

			//Type pltype = plugin.GetType("Namespace." + name);
			//Type pltype = plugin.GetType(name);
			Type[] types = plugin.GetTypes();
			Type mytype = null;

			//foreach(Type t in types) {
			//    if(t.Name == filename) {
			//        mytype = t;
			//        break;
			//    }
			//}

			bool typeFound = false;

			foreach(Type t in types) {
				if(t.Name.ToLower().Trim() == typeName.ToLower().Trim()) {
					mytype = t;
					typeFound = true;
					break;
				}
			}

			if(!typeFound) {
				throw new ComponentTypeNotFoundException(
					this._fileName,
					typeName
				);
			}

			object obj = Activator.CreateInstance(mytype);
			//comp = obj as T;

			return (T)obj;
		}

		/// <summary>
		/// Get the information about a plugin (independently of the plugin type).
		/// </summary>
		/// <param name="plugin"></param>
		/// <returns></returns>
		public PluginInfo GetPluginInfo(IPlugin plugin) {
			return new PluginInfo(
				plugin.PluginName,
				plugin.PluginDescription,
				plugin.PluginAuthor,
				plugin.PluginVersion
			);
		}

		/// <summary>
		/// Get the information about a plugin (independently of the plugin type).
		/// </summary>
		/// <param name="plugin"></param>
		/// <returns></returns>
		public PluginInfo GetPluginInfo(IPluginPrint plugin) {
			return new PluginInfo(
				plugin.PluginName,
				plugin.PluginDescription,
				plugin.PluginAuthor,
				plugin.PluginVersion
			);
		}

		/// <summary>
		/// Get the information about a plugin (independently of the plugin type).
		/// </summary>
		/// <param name="plugin"></param>
		/// <returns></returns>
		public PluginInfo GetPluginInfo(IPluginWindowForm plugin) {
			return new PluginInfo(
				plugin.PluginName,
				plugin.PluginDescription,
				plugin.PluginAuthor,
				plugin.PluginVersion
			);
		}

		/// <summary>
		/// Display the plugin information
		/// </summary>
		/// <param name="filename">The filename and path of the plugin</param>
		/// <exception cref="ArgumentNullException">If the parameter filename is null.</exception>
		public void DisplayPluginInfo(string filename) {
			if(filename == null) {
				throw new ArgumentNullException("filename", "The parameter filename cannot be null");
			}

			//IPlugin plug = null;

			Assembly plugin = Assembly.LoadFrom(filename);

			string name = plugin.GetName().ToString();
			string[] namelist = name.Split(',');
			name = namelist[0].Trim();

			//Type pltype = plugin.GetType("Namespace." + name);
			//Type pltype = plugin.GetType(name);
			Type[] types = plugin.GetTypes();
			//Type mytype = null;

			foreach(Type t in types) {
				if(t.Name == filename) {
					//
				}

				Console.WriteLine("");
				Console.WriteLine("Name: " + t.Name);
				Console.WriteLine("AssemblyQualifiedName: " + t.AssemblyQualifiedName);
				Console.WriteLine("BaseType: " + t.BaseType.ToString());
				Console.WriteLine("FullName: " + t.FullName);
				Console.WriteLine("Namespace: " + t.Namespace);
				Console.WriteLine("Module.FullyQualifiedName: " + t.Module.FullyQualifiedName);
				Console.WriteLine("");
			}

			//mytype = types[3];

			//switch(type) {
			//    case PluginType.CIOConfiguration:
			//        return null;

			//    case PluginType.SwecoinPrint:
			//        mytype = types[3];
			//        break;

			//    default:
			//        return null;
			//}

			//object obj = Activator.CreateInstance(mytype);
			//plug = obj as IPlugin;

			//return plug;
		}

		/// <summary>
		/// Load a plugin
		/// </summary>
		/// <param name="type">The type of the plugin</param>
		/// <param name="filename">The filename and path of the plugin</param>
		/// <param name="typename">The typename of the class to load</param>
		/// <exception cref="ArgumentNullException">If the parameter filename is null.</exception>
		private IPlugin _LoadPlugin(PluginType type, string filename, string typename) {
			if(filename == null) {
				throw new ArgumentNullException("filename", "The parameter filename cannot be null");
			}

			if(typename == null) {
				throw new ArgumentNullException("typename", "The parameter typename cannot be null");
			}

			IPlugin plug = null;

			Assembly plugin = Assembly.LoadFrom(filename);

			string name = plugin.GetName().ToString();
			string[] namelist = name.Split(',');
			name = namelist[0].Trim();

			//Type pltype = plugin.GetType("Namespace." + name);
			//Type pltype = plugin.GetType(name);
			Type[] types = plugin.GetTypes();
			Type mytype = null;

			//foreach(Type t in types) {
			//    if(t.Name == filename) {
			//        mytype = t;
			//        break;
			//    }
			//}

			foreach(Type t in types) {
				if(t.Name.Trim() == typename.Trim()) {
					mytype = t;
					break;
				}
			}

			//switch(type) {
			//    case PluginType.CIOConfiguration:
			//        return null;

			//    case PluginType.SwecoinPrint:
			//        mytype = types[3];
			//        break;

			//    default:
			//        return null;
			//}

			object obj = Activator.CreateInstance(mytype);
			plug = obj as IPlugin;

			return plug;
		}
	}
}
